#include"Map.h" 

#include"Field.h"
#include"FieldGenerator.h"
#include"Unit.h"

#include"AlgFloyd.h"

Map::Map(){
	m_fields = new Field**[NUM_ROWS];
	for(int i = 0; i < NUM_ROWS; i++){
		m_fields[i] = new Field*[NUM_COLS];
		for(int j = 0; j < NUM_COLS; j++)
			m_fields[i][j] = FieldGenerator::getField(i,j);
	}
	for(int i = 0; i < NUM_ROWS; i++){
		for(int j = 0; j < NUM_COLS; j++){
			Field *field = m_fields[i][j];
			for(int r = i-1; r < i+2; r++){
				if(r < 0 || r >= NUM_ROWS)
					continue;
				for(int c = j-1; c < j+2; c++){
					if(c < 0 || c >= NUM_COLS)
						continue;
					if(r == i && c == j)
						continue;
					if( ((i%2)==1 && r == j-1) || ((i%2)==0 && r == j+1) )//Makes a field have 6 rather than 8 neighbours
						continue;
					field->addNeighbour(m_fields[r][c]);
				}
			}
		}
	}
	m_floyd = new AlgFloyd(this);
	m_floyd->run();
}

Map::~Map(){
	for(int i = 0; i < NUM_ROWS; i++){
		for(int j = 0; j < NUM_COLS; j++)
			delete m_fields[i][j];
		delete [] m_fields[i];
	}
	delete [] m_fields;
	m_fields = 0;
	delete m_floyd;
	m_floyd = 0;
	
	m_turnSequence.clear();
	while(m_teamA.size())
		delete m_teamA.takeAt(0);
	while(m_teamB.size())
		delete m_teamB.takeAt(0);
	while(m_fallenUnits.size())
		delete m_fallenUnits.takeAt(0);
}
#include<stdio.h>
void Map::setTeams(QList<Unit*> teamA, QList<Unit*> teamB ){
	//Cleanup first...
	printf("Map::setTeams: cleanup\n");
	m_turnSequence.clear();
	while(m_teamA.size())
		delete m_teamA.takeAt(0);
	while(m_teamB.size())
		delete m_teamB.takeAt(0);
	while(m_fallenUnits.size())
		delete m_fallenUnits.takeAt(0);
	printf("Assigning teams to member variables.\n");
	m_teamA = teamA;
	m_teamB = teamB;
	printf("Populating team A\n");
	int i = NUM_ROWS/2 - teamA.size()/2;
	for(int j = 0; j < teamA.size(); j++){
		teamA[j]->setField(m_fields[i][0]);
		i++;
	}
	printf("Populating team B\n");
	i = NUM_ROWS/2 - teamB.size()/2;
	for(int j = 0; j < teamB.size(); j++){
		teamB[j]->setField(m_fields[i][NUM_COLS-1]);
		i++;
	}
	
	for(int i = 0; i < m_teamA.size(); i++)
		connect(m_teamA[i], SIGNAL(tookDamage()), this, SLOT(slotUnitTookDamage()));
	for(int i = 0; i < m_teamB.size(); i++)
		connect(m_teamB[i], SIGNAL(tookDamage()), this, SLOT(slotUnitTookDamage()));
	
	printf("InitTurns\n");
	initTurns();
	printf("Map::setTeams done\n");
}

QList<Unit*> Map::getEnemyUnits(Unit *unit)const{
	if(m_teamA.contains(unit))
		return m_teamB;
	else
		return m_teamA;
}

QList<Unit*> Map::getFriendlyUnits(const Unit *unit, bool includeSelf)const{
	QList<Unit*> ret;
	if(m_teamA.contains(const_cast<Unit*>(unit)))
		ret = m_teamA;
	else
		ret = m_teamB;
	if(!includeSelf)
		ret.removeOne(const_cast<Unit*>(unit));
	return ret;
}

QList<Field*> Map::getAccessibleFields(const Unit *unit)const{
// 	printf("Map::getAccessibleFields(%lx)\n", (long unsigned)unit);
	QList<Field*> ret;
	QList<int> dist;
	QList<bool> occupied;
	ret.append(unit->field()); dist.append(0);occupied.append(false);
	int expandIndex = 0;
	bool changed = true;
// 	printf("\ta\n");
	while(changed){
		changed = false;
		for(int i = expandIndex; i < ret.size(); i++){
// 			printf("\tb%d\n", i);
			Field *toExpand = ret[i];
// 			printf("Field: %lx\n", (long unsigned)toExpand);
			for(int j = 0; j < toExpand->neighbours().size(); j++){
// 				printf("\tX%d\n", i);
				Field *toAdd = toExpand->neighbours()[j];
// 				printf("\tb'%d\n", i);
				if(ret.contains(toAdd) || (toAdd->occupiedBy() && toAdd->occupiedBy()->team() != unit->team()))
					continue;
				bool o = toAdd->occupiedBy() != 0;
// 				printf("\tb''%d\n", i);
				int d = dist[i] + toAdd->movementCost();
				if(d > unit->speed())
					continue;
// 				printf("\tb'''%d\n", i);
				int k = expandIndex;
				while(k < ret.size()){
// 					printf("\tb''''%d\n", i);
					if(d < dist[i])
						break;
					k++;
				}
// 				printf("\tc%d%d\n", i, j);
				ret.insert(k, toAdd);
				dist.insert(k, d);
				occupied.insert(k, o);
				changed = true;
			}
		}
// 		printf("\td\n");
		expandIndex++;
	}
	for(int i = 0; i < ret.size(); i++){
		if(occupied[i]){
			occupied.takeAt(i);
			dist.takeAt(i);
			ret.takeAt(i);
			i--;
		}
	}
// 	printf("\te\n");
	return ret;
}

void Map::moveUnit(Unit *unit, Field *dest){
	unit->field()->setOccupiedBy(0);
	unit->setField(dest);
	dest->setOccupiedBy(unit);
}

void Map::remove(Unit *unit){
// 	printf("Map::remove\n");
	if(m_teamA.contains(unit))
		m_teamA.removeOne(unit);
	else if(m_teamB.contains(unit))
		m_teamB.removeOne(unit);
	m_turnSequence.removeOne(unit);
// 	printf("\ta\n");
	unit->field()->setOccupiedBy(0);
// 	printf("\tb\n");
	unit->setField(0);
// 	printf("\tc\n");
	m_fallenUnits.push_back(unit);
	emit unitDied(unit);
// 	printf("~Map::remove\n");
}

void Map::initTurns(){
	QList< QList<Unit*> > units;
	for(int i = 0; i < m_teamA.size(); i++){
		int j = 0;
		while(j < units.size()){
			if(m_teamA[i]->speed() == units[j][0]->speed()){
				units[j].push_back(m_teamA[i]);
				break;
			}else if(m_teamA[i]->speed() > units[j][0]->speed()){
				units.insert(j, QList<Unit*>());
				units[j].push_back(m_teamA[i]);
				break;
			}else
				j++;
		}
		if(j==units.size()){
			units.push_back( QList<Unit*>() );
			units[j].push_back(m_teamA[i]);
		}
	}
	for(int i = 0; i < m_teamB.size(); i++){
		int j = 0;
		while(j < units.size()){
			if(m_teamB[i]->speed() == units[j][0]->speed()){
				units[j].push_back(m_teamB[i]);
				break;
			}else if(m_teamB[i]->speed() > units[j][0]->speed()){
				units.insert(j, QList<Unit*>());
				units[j].push_back(m_teamB[i]);
				break;
			}else
				j++;
		}
		if(j==units.size()){
			units.push_back( QList<Unit*>() );
			units[j].push_back(m_teamB[i]);
		}
	}
	int teamACount = 0;
	int teamBCount = 0;
	while(units.size()){
		if(units[0].size()==0)
			units.pop_front();
		else{
			if(teamACount == 0 && teamBCount == 0){
				for(int i = 0; i < units[0].size(); i++){
					if(units[0][i]->team() == TEAM_B)
						teamBCount++;
					else
						teamACount++;
				}
			}
			
			Team toBeAdded = teamBCount > teamACount ? TEAM_B : TEAM_A;
			for(int i = 0; i < units[0].size(); i++){
				if(units[0][i]->team() == toBeAdded){
					m_turnSequence.push_back(units[0].takeAt(i));
					if(teamBCount > teamACount)
						teamBCount--;
					else
						teamACount--;
					break;
				}
			}
		}
	}
}

bool Map::doTurn(){
	m_turnSequence[0]->doAIMove();
	emit turnEnded(m_turnSequence[0]);
	m_turnSequence.push_back( m_turnSequence.takeAt(0) );
// 	printf("Remaining:\tA: %d\tB: %d\n", m_teamA.size(), m_teamB.size());
	return m_teamA.size() && m_teamB.size();
}

void Map::slotUnitTookDamage(){
	Unit *unit = (Unit*)sender();
	emit unitDamaged(unit);
}

int Map::travelDistance(const Field *from, const Field *to)const{
	int ret = m_floyd->cost(from->row(), from->col(), to->row(), to->col());
// 	printf("Distance from (%d,%d) to (%d,%d) is %d\n", from->col(), from->row(), to->col(), to->row(), ret);
	return ret;
}
